/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#include "Projectile.hpp"

#include "Landscape.hpp"
#include "UnitInvariant.hpp"
#include "World.hpp"

namespace spelprog2 {
namespace sim {

Projectile::Projectile(const ProjectileInvariant & invariant, const Int3d & initPosition,
        const Int3d & initVelocity, Int healthDamage, Int moraleDamage) :
    invariant_(&invariant),
    position_(initPosition),
    velocity_(initVelocity),
    healthDamage_(healthDamage),
    moraleDamage_(moraleDamage),
    id_(-1)
{
}

Projectile & Projectile::create(World & world, const ProjectileInvariant & invariant, const Int3d & initPosition,
    const Int3d & initVelocity, Int healthDamage, Int moraleDamage)
{
    Projectile & p = world.addProjectile(Projectile(invariant, initPosition, initVelocity, healthDamage, moraleDamage));
    world.getSignals().projectileAdded(p);
    return p;
}

bool Projectile::simUpdate(World & world, Int sequenceNbr)
{
    const Int PROJECTILE_MAX_FALL_SPEED = 10000;

    //update vertical speed
    velocity_.y -= PROJECTILE_GRAVITY;
    if (velocity_.y < -PROJECTILE_MAX_FALL_SPEED) {
        velocity_.y = -PROJECTILE_MAX_FALL_SPEED;
    }
    //for now no horizontal speed decrease

    //update position
    position_ += velocity_;

    //check for out of bounds position - if so, remove projectile
    const LandscapeSpatials & spatials = world.getLandscape().getSpatials();
    if (    position_.x < Int(0) ||
            position_.z < Int(0) ||
            position_.x >= spatials.getExtents().x ||
            position_.z >= spatials.getExtents().y) {

        world.getSignals().projectileRemoved(*this);
        return false;
    }

    //see if projectile has touched ground - do damage (if any) and remove
    Int2d pos2d;
    pos2d.x = position_.x;
    pos2d.y = position_.z;
    if (position_.y <= spatials.getAltitude(pos2d)) {
        typedef std::list<Unit::Pointer> UnitList;
        UnitList units;
        world.getUnits().find(pos2d, *std::back_inserter(units)); //if we add splash damage this must be changed to area version

        //look up potentially hurt units
        for (UnitList::iterator it = units.begin(), end = units.end(); it != end; ++it) {
            Unit::Pointer unit = *it;

            WideInt squareRadius = unit->getInvariant().getCollisionRadius();
            squareRadius *= squareRadius;
            if ((unit->getPosition() - pos2d).squaredLength() <= squareRadius) {
                //just hurt a single unit for now, no splash damage yet
                unit->damage(healthDamage_, moraleDamage_);
                break;
            }
        }

        //remove projectile either way
        world.getSignals().projectileRemoved(*this);
        return false;
    }

    //keep going
    return true;
}

}; //sim
}; //spelprog
